#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <termios.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>

#define SERVER_ADDRESS "127.0.0.1"
#define PORT 8080
#define BUFFER_SIZE 1024
#define SHM_SIZE sizeof(shared_mem)

typedef struct
{
    int loginflag;
    int isrunning;
} shared_mem;

void getpassword(char *password)
{
    struct termios old, new;
    tcgetattr(STDIN_FILENO, &old);
    new = old;
    new.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON);
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &new);

    int i = 0;
    char ch;

    while ((ch = getchar()) != '\n' && i < 99)
    {
        if (ch == 127)
        { // 处理退格键
            if (i > 0)
            {
                putchar(8);   // 光标回退一格
                putchar(' '); // 覆盖掉原来的字符
                putchar(8);   // 光标回退一格
                i--;
            }
        }
        else
        {
            password[i++] = ch;
            putchar('*');
        }
    }

    password[i] = '\0';
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
}

int main()
{
    int sock = 0, valread;
    struct sockaddr_in server_address;
    char buffer[BUFFER_SIZE] = {0};
    char response[BUFFER_SIZE] = {0};
    char message[BUFFER_SIZE] = {0};
    char msg_recv[BUFFER_SIZE] = {0};
    int recv;
    int user_id;
    int friend_id;
    char username[10], friendname[10], pwd[10], pwd2[10];
    pid_t pid, c_pid;

    int shm_id;
    key_t key = 1234;
    shared_mem *shm_addr;

    // 创建共享内存段
    if ((shm_id = shmget(key, SHM_SIZE, IPC_CREAT | 0666)) < 0)
    {
        perror("shmget");
        exit(1);
    }
    // 将共享内存段附加到进程地址空间
    if ((shm_addr = shmat(shm_id, NULL, 0)) == (void *)-1)
    {
        perror("shmat");
        exit(1);
    }

    // 初始化共享内存区
    shm_addr->loginflag = 0;
    shm_addr->isrunning = 1;

    // 连接服务器
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Socket creation error \n");
        return -1;
    }
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(PORT);
    if (inet_pton(AF_INET, SERVER_ADDRESS, &server_address.sin_addr) <= 0)
    {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }
    if (connect(sock, (struct sockaddr *)&server_address, sizeof(server_address)) < 0)
    {
        printf("\nConnection Failed \n");
        return -1;
    }

    pid = fork();
    if (pid == -1)
    {
        printf("fork error.\n");
    }
    if (pid == 0)
    {
        // 显示菜单
        printf("1. Login\n");
        printf("2. Register\n");
        printf("3. Add friend\n");
        printf("4. Send message\n");
        printf("5. Broadcast\n");
        printf("6. Exit\n");
        while (shm_addr->isrunning)
        {
            usleep(100000);
            printf("Enter your choice:>");
            int choice;
            scanf("%d", &choice);
            // 清空输入缓存
            while ((getchar()) != '\n')
                ;

            if (choice == 1)
            {
                // 登录，获取用户名和密码
                printf("Enter username:");
                scanf("%s", username);
                getchar();
                printf("Enter password:");
                getpassword(pwd);
                sprintf(buffer, "login %s %s", username, pwd);
                send(sock, buffer, strlen(buffer), 0);
                printf("%s", response);
                sleep(1);
            }
            else if (choice == 2)
            {
                // 获取用户名和密码
                printf("Enter your username:");
                scanf("%s", username);
                getchar();
                printf("Enter your password:");
                getpassword(pwd);
                printf("Ensure your password:");
                getpassword(pwd2);
                if (strcmp(pwd, pwd2) == 0)
                {
                    sprintf(buffer, "register %s %s", username, pwd);
                    send(sock, buffer, strlen(buffer), 0);
                }
                else
                {
                    printf("You typed different password, register failed\n");
                }
            }
            else if (choice == 3)
            {
                // 添加好友
                if (shm_addr->loginflag == 0)
                {
                    printf("Please login first!\n");
                }
                else
                {
                    printf("Enter friend name: ");
                    scanf("%s", friendname);
                    getchar();
                    sprintf(buffer, "add_friend %s %s", username, friendname);
                    send(sock, buffer, strlen(buffer), 0);
                }
            }
            else if (choice == 4)
            {
                // 发送消息
                if (shm_addr->loginflag == 0)
                {
                    printf("Please login first!\n");
                }
                else
                {
                    printf("Enter friend name: ");
                    scanf("%s", friendname);
                    getchar();
                    printf("Now you are chat with %s.(Input q\\Q to end this chat.)\n", friendname);
                    while (1)
                    {
                        printf("Enter message: ");
                        fgets(message, BUFFER_SIZE, stdin);
                        // 去掉末尾的回车符
                        int len = strlen(message);
                        if (len > 0 && message[len - 1] == '\n')
                        {
                            message[len - 1] = '\0';
                        }
                        sprintf(buffer, "send_message %s %s %s", username, friendname, message);
                        send(sock, buffer, strlen(buffer), 0);
                        if (strcmp(message, "q") == 0 | strcmp(message, "Q") == 0)
                        {
                            break;
                        }
                    }
                }
            }
            else if (choice == 5)
            {
                if (shm_addr->loginflag == 0)
                {
                    printf("Please login first!\n");
                }
                else
                {
                    printf("Enter message: ");
                    fgets(message, BUFFER_SIZE, stdin);
                    // 去掉末尾的回车符
                    int len = strlen(message);
                    if (len > 0 && message[len - 1] == '\n')
                    {
                        message[len - 1] = '\0';
                    }
                    sprintf(buffer, "broadcast %s %s", username, message);
                    send(sock, buffer, strlen(buffer), 0);
                }
            }
            else if (choice == 6)
            {
                shm_addr->isrunning = 0;
                printf("Exiting...\n");
                fflush(stdout); // 刷新缓冲区，使输出立即显示
                kill(getppid(), SIGTERM);
                break;
            }
            else
            {
                // 无效输入
                printf("Invalid choice\n");
            }
        }
        shmdt((void *)shm_addr);
        shmctl(shm_id, IPC_RMID, NULL);
        close(sock);
        exit(-1);
    }
    else
    {
        // 记录子进程 ID
        c_pid = pid;
        while (shm_addr->isrunning)
        {
            recv = read(sock, msg_recv, BUFFER_SIZE);
            if (recv == 0)
            {
                close(sock);
                printf("Server down.\n");
                kill(pid, SIGTERM);
                shm_addr->isrunning = 0;
                break;
            }
            msg_recv[recv] = '\0';
            if (strcmp(msg_recv, "login successfully!") == 0)
            {
                shm_addr->loginflag = 1;
            }
            if (strcmp(msg_recv, "disconnect") == 0)
            {
                printf("You are kicked by server.\n");
                kill(pid, SIGTERM);
                shm_addr->isrunning = 0;
                break;
            }
            printf("\n>>>%s\n", msg_recv);
        }
        waitpid(c_pid, NULL, 0);
        shmdt((void *)shm_addr);
        shmctl(shm_id, IPC_RMID, NULL);
        close(sock);
    }
    return 0;
}
